AMÉLIORER LE PRODUIT IA DE VOTRE START-UP¶

SYNTHESE GRAPHIQUE DES RESULTATS¶
L'objectif ici est de synthétiser les principaux résultats de l'analyse :
- des commentaires négatifs pour détecter les sujets d'insatisfaction
- des photos pour détecter leur catégorie
(Pour plus de détail et pour parcourir les différentes étapes ayant permis d'atteindre ces résultats, se référer au notebook principal)
# imports
# custom functions
import myFunctions as mf
%load_ext autoreload
%autoreload 2
# LDA
from gensim.models.ldamodel import LdaModel
# pyLDAvis
import pyLDAvis.gensim_models as gensimvis
import pyLDAvis
# file and directory management
import os
# to make saves
from joblib import dump, load
# data viz
from matplotlib import pyplot as plt
%matplotlib inline
import seaborn as sns
# image annotations
from matplotlib.offsetbox import OffsetImage, AnnotationBbox
from matplotlib.colors import ListedColormap, to_rgb
PARTIE I - ANALYSER LES COMMENTAIRES NÉGATIFS POUR DÉTECTER LES DIFFÉRENTS SUJETS D’INSATISFACTION¶
NLP - Méthode¶
L'objectif final est de détecter les sujets de mécontentement dans les avis qui seront postés sur Avis Restau. Le but ici n'est pas de réaliser ce projet entièrement mais de tester sa faisabilité. Pour cela nous avons la chance de disposer d'un jeu de données existant disponible sur la plateforme Yelp.
Nous réaliserons donc notre études préliminaire sur ce dataset :
- Nous réaliserons les étapes préalables à l'entraînement d'une algorithme de détection de sujets :
- sélectionner quelques milliers de commentaires négatifs
- pré-traiter ces données :
- mettre en minuscule,
- tokeniser,
- normaliser les mots via une lemmatisation,
- etc.
- création du dictionnaire et représentation de nos reviews en vecteurs bag-of-words
- adapter la pondération des mots grâce à un TF-IDF
- Nous testerons alors la faisabilité sur ce petit échantillons :
- utiliser une technique de réduction de dimension de type topic-modeling pour extraire nos sujets d'insatisfaction sous-jacents de notre corpus. Nous utiliserons ici le
Latent Dirichlet Allocation (LDA) - évaluer notre réduction afin de choisir le bon hyperparamètre
num_topics
- utiliser une technique de réduction de dimension de type topic-modeling pour extraire nos sujets d'insatisfaction sous-jacents de notre corpus. Nous utiliserons ici le
- Enfin nous tâcherons de visualiser nos topics afin d'en détecter les mots-clés. Pour cela nous utiliserons 2 librairies :
wordcloudpyLDAvis
NLP - Nos commentaires et leur nettoyage¶
Étapes du preprocessing :
- charger un échantillon de reviews
- mettre en minuscules
- supprimer les url
- supprimer les séquences d'échappement
- corriger les mots avec des caractères répétés
- cleaning général :
- supprimer les ponctuations
- supprimer les stopwords
- supprimer les stopwords spécifiques métier
- supprimer les nombres et les nombres écrits en lettres
- filtrer sur les POS (en ne gardant que les adjectifs et noms)
- normaliser les mots (ici : lemmatisation)
# load existing .joblib
rev = load("mySaves/rev/rev.joblib")
rev
| text | lower | withoutURL | withoutEscSeq | withoutDupl | textClean | tokensClean | |
|---|---|---|---|---|---|---|---|
| 1947 | I came here for lunch and was very disappointe... | i came here for lunch and was very disappointe... | i came here for lunch and was very disappointe... | i came here for lunch and was very disappointe... | i came here for lunch and was very disappointe... | disappointed bland expensive quality food luke... | [disappointed, bland, expensive, quality, food... |
| 5136 | One star for really nice service + no wait (ca... | one star for really nice service + no wait (ca... | one star for really nice service + no wait (ca... | one star for really nice service + no wait (ca... | one star for really nice service + no wait (ca... | star nice service wait hour bad meal grit mass... | [star, nice, service, wait, hour, bad, meal, g... |
| 5803 | Fried rice looks like brown rice about 2 1/4 i... | fried rice looks like brown rice about 2 1/4 i... | fried rice looks like brown rice about 2 1/4 i... | fried rice looks like brown rice about 2 1/4 i... | fried rice looks like brown rice about 2 1/4 i... | rice brown rice chunk egg vegtible water gun s... | [rice, brown, rice, chunk, egg, vegtible, wate... |
| 3459 | I agree with some of the other commenters, the... | i agree with some of the other commenters, the... | i agree with some of the other commenters, the... | i agree with some of the other commenters, the... | i agree with some of the other commenters, the... | commenter okay worth hype food good appetizer ... | [commenter, okay, worth, hype, food, good, app... |
| 7301 | I used the mobile app, the bill was for $31 a... | i used the mobile app, the bill was for $31 a... | i used the mobile app, the bill was for $31 a... | i used the mobile app, the bill was for $31 a... | i used the mobile app, the bill was for $31 a... | mobile app bill friend store order card time i... | [mobile, app, bill, friend, store, order, card... |
| ... | ... | ... | ... | ... | ... | ... | ... |
| 196744 | Passing through Reno we stopped to get a bean ... | passing through reno we stopped to get a bean ... | passing through reno we stopped to get a bean ... | passing through reno we stopped to get a bean ... | passing through reno we stopped to get a bean ... | reno bean burrito good rating yelp long time d... | [reno, bean, burrito, good, rating, yelp, long... |
| 199082 | For over an hour we were told we were next. Wa... | for over an hour we were told we were next. wa... | for over an hour we were told we were next. wa... | for over an hour we were told we were next. wa... | for over an hour we were told we were next. wa... | hour table staff food chance bloody mary bar a... | [hour, table, staff, food, chance, bloody, mar... |
| 197735 | If you're on a clock, this is not the place fo... | if you're on a clock, this is not the place fo... | if you're on a clock, this is not the place fo... | if you're on a clock, this is not the place fo... | if you're on a clock, this is not the place fo... | clock food good minute order good benefit doub... | [clock, food, good, minute, order, good, benef... |
| 191503 | Stopped in for dinner break as I work in the p... | stopped in for dinner break as i work in the p... | stopped in for dinner break as i work in the p... | stopped in for dinner break as i work in the p... | stopped in for dinner break as i work in the p... | dinner break promenade turkey burger good reas... | [dinner, break, promenade, turkey, burger, goo... |
| 191338 | Ducked in to meet a local politician and talk ... | ducked in to meet a local politician and talk ... | ducked in to meet a local politician and talk ... | ducked in to meet a local politician and talk ... | ducked in to meet a local politician and talk ... | local politician social digital marketing quai... | [local, politician, social, digital, marketing... |
10000 rows × 7 columns
NLP - Dictionnaire, BOW et TF-IDF¶
Nota : Par rapport à l'exemple ci-dessus, nous avons intégré plus de commentaires : 60000.
Étapes de traitement :
- création dictionnaire
- filtre sur la fréquence pour les mots rare et et pour les mots fréquents
- conversion des documents en bag-of-words
- pondération des documents par TF-IDF
# load existing .joblib
tfidf_vector = load("mySaves/nlpDictAndVector/tfidf_vector.joblib")
dictionary = load("mySaves/nlpDictAndVector/dictionary.joblib")
Exemple de document après le traitement :
display(tfidf_vector[16])
[(29, 0.09775788015223455), (115, 0.31639656093164764), (129, 0.22861969373276309), (233, 0.4524697946579984), (234, 0.4096645361564042), (235, 0.6038715712381316), (236, 0.31757648988015796)]
NLP - Latent Dirichlet Allocation et visualisations¶
Choix du nombre de topics basé sur le score de cohérence :
if "coherencesPlot.joblib" in os.listdir("mySaves/coherencesPlot") :
# load existing .joblib
load("mySaves/coherencesPlot/coherencesPlot.joblib")
Il n'y a pas vraiment de pic ... Nous observons néanmoins une chute de cohérence après 5 topics. Nous allons regarder ce que cela donne pour 3, 4 et 5 topics :
NLP - LDA - 3 topics¶
# create the LDA model
model = LdaModel(
corpus=tfidf_vector,
num_topics=3,
id2word=dictionary,
random_state=16
)
Vue wordcloud et score de cohérence :
# use custom function wordCloudAndCoherence to display 1 wordcloud for each topic and compute coherence_score
mf.wordCloudAndCoherence(model=model, corpus=tfidf_vector)
Les limites d'une telle visualisation :
- ne nous permet pas vraiment de comprendre le degré de différence entre les topics
- apporte du biais aléatoire dans l'interprétation de l'importance des mots (orientation, position, couleur)
- ne nous permet pas de visualiser l'importance relative des topics
Voyons ce que cela donne avec PyLDAvis :
pyLDAvis est une librairie spécialisée permettant de :
- avoir une vue d'ensemble d'un modèle de topic modeling
- explorer en détail chaque topic
- explorer en détail chaque mot par rapport aux topics
Il est composé de plusieurs parties :
- Intertopic Distance Map :
- représentation 2D des topics dans l'espace des probabilités de mots dans les topics (matrice topics/mots issue du modèle de LDA). Cette réduction de dimension 2D est issue d'un MDS (multidimensional scaling)
- la représentation de chaque topic dans cet espace réduit permet ainsi d'apprécier les distances qui les séparent les uns des autres dans l'espace plus large, et donc s'ils sont bien différents
- la taille de chaque topic est elle liée à l'espace des probabilités des topics dans les documents (matrice documents/topics issue du modèle LDA). La somme des probabilités sur tous les documents de chaque topic permet de définir sa taille. Elle représente donc la prédominance du topic dans le corpus
- liste des mots les plus importants :
- sans spécifier un topic : Top-30 Most Salient Terms :
- mots classés selon la saliency : liée à la fréquence globale de chaque mot dans le corpus.
- barres bleus : fréquences à l'échelle du corpus
- en sélectionnant un topic : Top-30 Most Relevant Terms for Topic n :
- mots classés selon la relevance : liée à la probabilité de chaque mot dans un topic particulier. Ce classement évolue en fonction du paramètre λ :
- λ tend vers 1 : la relevance met plus en avant les mots les plus présents au sein du topic
- λ tend vers 0 : la relevance normalise cette probabilité du mot pour ce topic avec la probabilité du mot au global dans le corpus. On voit alors apparaître non pas les mots forcément les plus présents, mais les mots les plus distinctifs du topic analysé
- barres rouges : fréquences estimées au sein du topic
- mots classés selon la relevance : liée à la probabilité de chaque mot dans un topic particulier. Ce classement évolue en fonction du paramètre λ :
- il est aussi possible de sélectionner un mot en particulier : les tailles des topics de l'Intertopic Distance Map représentent alors seulement la répartion de ce mot choisi aux sein des topics.
- sans spécifier un topic : Top-30 Most Salient Terms :
# use the "prepare" function
vis_data = gensimvis.prepare(
topic_model=model,
corpus=tfidf_vector,
dictionary=dictionary
)
# display
pyLDAvis.display(vis_data)
Les 3 sujets de mécontentement :
- le comportement du personnel et le temps d'attente, avec des mots comme table, server, minute, hour, rude, manager, experience, incorrect, customer, hostess, staff, etc.
- le rapport qualité / prix, avec des mots comme flavor, bland, dry, taste, price, portion, small, etc.
- la livraison, avec des mots comme delivery, order, hour, service, slow, min, minute, driver, phone, refund, etc.
NLP - LDA - 4 topics¶
# create the LDA model
model = LdaModel(
corpus=tfidf_vector,
num_topics=4,
id2word=dictionary,
random_state=16
)
Vue wordcloud et score de cohérence :
# use custom function wordCloudAndCoherence to display 1 wordcloud for each topic and compute coherence_score
mf.wordCloudAndCoherence(model=model, corpus=tfidf_vector)
Vue PyLDAvis :
# use the "prepare" function
vis_data = gensimvis.prepare(
topic_model=model,
corpus=tfidf_vector,
dictionary=dictionary
)
# display
pyLDAvis.display(vis_data)
les 4 sujets se distinguent un peu plus :
- le comportement du personnel et le temps d'attente, avec des mots comme minute, reservation, order, manager, rude, hour, employee, server, waitress, hostess, service, time, busy, attitude, etc.
- la qualité du plat et son prix, avec des mots comme flavor, dry, bland, salty, quality, taste, poisonning, soggy, price, portion, small, etc.
- l'hygiène, avec des mots comme dirty, flour, filthy, clean, hair, glove, mask, fly, bathroom, cleanliness, cockroach, rat, restroom, toilet, disgusting, bug, etc.
- la livraison, avec des mots comme driver, delivery, uber, masked, order, drive, time, hour, slow, cold,etc.
NLP - LDA - 5 topics¶
# create the LDA model
model = LdaModel(
corpus=tfidf_vector,
num_topics=5,
id2word=dictionary,
random_state=16
)
Vue wordcloud et score de cohérence :
# use custom function wordCloudAndCoherence to display 1 wordcloud for each topic and compute coherence_score
mf.wordCloudAndCoherence(model=model, corpus=tfidf_vector)
Vue PyLDAvis :
# use the "prepare" function
vis_data = gensimvis.prepare(
topic_model=model,
corpus=tfidf_vector,
dictionary=dictionary
)
# display
pyLDAvis.display(vis_data)
Les 5 sujets de mécontentement :
- l'ambiance avec des mots comme table, reservation, music, loud, party, birthday, noisy, dance, etc.
- l'attitude du personnel et le temps d'attente, avec des mots comme table, delivery, understaffed, reservation, minute, order, manager, rude, hour, employee, waitress, service, time, busy, attitude, etc.
- 2 sujets très proches sur la nourriture, avec des mots en commun comme flavor, soggy, dry, taste, etc. :
- l'un un peu plus axé sur la qualité du plat et son prix, avec des mots comme portion, price, small, quality, taste, etc.
- l'autre se concentrant plus exclusivement sur la nourriture
- l'hygiène, avec des mots comme dirty, filthy, bathroom, glove, floor, mask, health, restroom, toilet, disgusting, rat, cleaning, roach, hair, etc.
NLP - Conclusion¶
Il est difficile de conclure sur le nombre idéal de topics :
- se contenter de 3 met de côté l'hygiène
- conserver 4 topics est intéressant car les sujets sont clairs, avec l'hygiène qui se rajoute,
- enfin avec 5 topics nous avons réussi à obtenir un sujet différent avec l'ambiance, mais la livraison disparaît et le rapport qualité/prix se divise en 2 sous-thèmes pas très clairs...
Nous pouvons néanmoins conclure sur le faisabilité du projet :
- le fait d'ajouter plus de commentaires et d'affiner la préparation des données a permis de trouver de vrais sujets de mécontentement,
- il est donc tout à fait possible :
- que l'utilisation de toute la base de données nous permette de faire disparaître les limitations que nous avons observé
- qu'une recherche plus poussée des meilleures paramètres de notre pipeline (
gridsearchCV,BayesSearchCV, etc.) permette d'améliorer le modèle:- filtrage,
- liste de stopwords,
- le nombre de topics,
- les autres hyper-paramètres du modèle (
alphaetetanotamment, qui conditionne le côté plus ou moins lisse des distributions de chaque document en topics et de chaque topic en mots)
- cependant il faut noter que :
- nous avons dû ajouter "à la main" beaucoup de stopwords spécifiques... A voir si ce procédé peut d'une quelconque manière être évité...
- après quelques essais il s'avère que les thèmes détectés par le modèle sont instables (notamment en fonction de la liste des stopwords spécifiques)
Il serait intéressant, dans le cadre de la poursuite du projet, de tester d'autres modèles (LSA? BERTopic? autre ?).
PARTIE II.A - ANALYSER LES PHOTOS POUR DÉTERMINER LES CATÉGORIES DES PHOTOS - SIFT¶
CV-SIFT - Méthode¶
Nous avons la chance que les photos Yelp soient déjà labellisées : L'objectif final est d'entraîner un algorithme de classification supervisée sur celles-ci afin d'utiliser le modèle ultérieurement sur les photos qui seront publiées sur Avis Restau.
Le but ici n'est pas de réaliser ce projet entièrement. L'objectif préliminaire est d'analyser les photos pour vérifier la faisabilité :
Nous réaliserons donc toutes les étapes préalables à l'entraînement d'un algorithme de classification :
- réaliser le pré-traitement sur N images
- extraire les features (nous utiliserons le SIFT) pour obtenir les descripteurs de nos N images
- création des bag-of-visual-words :
- trouver les "visual words" grâce à un algorithme KMeans appliqué sur l'ensemble des descripteurs trouvés
- construire l'histogramme de chaque image en fonction de ce dictionnaire de "visual-words"
- contruire ainsi notre matrice des "bag-of-visual-words", de taille N images X taille dictionnaire
- adapter la pondération des visual-words grâce à un TF-IDF
... mais au lieu de réaliser ce travail sur un très grand nombre de photos (avec un split préalable en train/test set, etc.), nous ne prendrons que quelques photos de chaque catégorie.
Nous testerons alors la faisabilité sur ce petit échantillon avec un clustering KMeans avec K = le nombre de labels pour vérifier que nos features peuvent bien regrouper nos labels :
- réduction de dimension préalable via une ACP
- appliquer le KMeans
- effectuer une mesure de similarité ARI
- analyser les catégories les mieux regroupées (via une matrice de confusion entre les catégories et les clusters prédits)
Nous tâcherons également de visualiser nos N images dans cet espace des features pour analyser si les catégories/labels fournies ressortent :
- utilisation du même espace réduit par l'ACP
- réduction de dimension par T-SNE, idéal pour représenter des données en grande dimension
- visualisation :
- des catégories
- des clusters
CV-SIFT - Nos image et leur traitement¶
Étapes du preprocessing :
- charger un échantillon d'images
- mettre en nuance de gris
- égalisation d'histogramme
- filtre gaussien
- extraire les features descripteurs SIFT
Nous pouvons regarder ce que cela donne visuellement :
# load existing .joblib
examplesPhotoDict = load("mySaves/examplesPhotoDict/examplesPhotoDict.joblib")
# create a figure
fig, axs = plt.subplots(2,5, figsize=(14,6))
# choose a category example
cat = "drink"
# plot the two images stored in the dict for this category, for each preprocessing step
for i,phase in enumerate(['raw', 'gray', 'equal', 'gaussian', 'keypoints']) :
for j,img in enumerate(examplesPhotoDict[phase][cat]) :
# plot
# handle grayscale
if len(img.shape) == 2 :
axs[j,i].imshow(img, cmap='gray',vmin=0,vmax=255)
else :
axs[j,i].imshow(img)
# set anchor
axs[j,i].set_anchor("N")
# remove axis
axs[j,i].axis(False)
# title on top axes
if j==0 :
axs[j,i].set_title(phase)
# sup title
fig.suptitle("Photo cleaning steps")
plt.show()
CV-SIFT - Dictionnaire, Bag-of-Visual-Words, TF-IDF, PCA¶
Étapes de traitement :
- Création d'un dictionnaire de visual words :
- rassemblement de tous les descripteurs des images
- utilisation d'un algorithme de clustering (
MiniBatchKMeans) : chaque centroïde de cluster correspond à un visual word
- Création matrice des Bag-of-Visual-Words :
- pour chaque descripteur de chaque image, appliquer le modèle de clustering (dictionnaire) pour attibuer le visual word correspondant
- pour chaque image, compter le nombre d'occurences de chaque visual word présent
- intégrer ces histogrammes à une matrice (nombre d'images X nombre de visual words)
- Appliquer la pondération TF-IDF
- Appliquer une analyse en composantes principales PCA pour :
- réduire le nombre de dimension (curse of dimensionality)
- faciliter le travail du futur modèle (temps et mémoire)
Nous avons alors un dataframe comportant une ligne par image et une colonne par composante principale :
# load existing .joblib
siftFeatures = load("mySaves/siftFeatures/siftFeatures.joblib")
siftFeatures
| C0 | C1 | C2 | C3 | C4 | C5 | C6 | C7 | C8 | C9 | ... | C599 | C600 | C601 | C602 | C603 | C604 | C605 | C606 | C607 | C608 | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | -0.079165 | -3.800291 | 14.066756 | 0.984657 | 5.144575 | 8.648161 | -7.180452 | -2.898898 | 1.124841 | -4.255499 | ... | 0.102363 | 0.020066 | -0.124666 | -0.117224 | 0.042325 | -0.562461 | 0.590468 | -0.214824 | 0.246502 | -0.510977 |
| 1 | 4.668302 | -1.241076 | -3.295023 | 2.710035 | -2.393686 | 1.030828 | 7.408102 | 2.990802 | -2.122968 | -2.902438 | ... | -0.028090 | -0.369633 | -0.578845 | -0.095885 | 0.317662 | 0.184974 | -0.006934 | -0.003246 | -0.325203 | -0.115126 |
| 2 | -0.501951 | -5.224448 | -5.325085 | 3.149939 | 1.008314 | -1.202440 | 5.776378 | -2.664012 | -2.610973 | -1.214457 | ... | -0.278282 | 0.357192 | 0.327380 | 0.206463 | -0.174073 | -0.166300 | 0.212760 | -0.036203 | -0.078368 | -0.545426 |
| 3 | -7.326880 | -9.606032 | -3.030050 | -0.454922 | -1.654513 | -1.160068 | -0.884439 | 2.996916 | -3.937234 | 0.063916 | ... | 0.034189 | 0.162919 | -0.282017 | 0.042890 | -0.397165 | -0.154988 | -0.453393 | 0.653721 | -0.202591 | 0.492679 |
| 4 | 1.945519 | -2.702972 | 0.182178 | 1.762309 | -2.828546 | 6.268726 | -0.672833 | -0.646871 | 1.104524 | -6.210130 | ... | 0.132200 | -0.208414 | 0.130558 | -0.156949 | -0.338585 | 0.188776 | 0.126383 | 0.226559 | 0.037979 | 0.339935 |
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
| 745 | -0.748908 | 3.689911 | -3.130025 | -4.271580 | -1.739386 | -4.707257 | -3.908305 | -0.412259 | 4.291135 | -0.658773 | ... | -0.072263 | -0.949471 | 0.220239 | -0.276538 | 0.534357 | -0.243438 | 0.089219 | 0.116614 | -0.029192 | 0.164529 |
| 746 | -0.979159 | -2.489993 | -2.394485 | -3.009860 | -3.376783 | 5.438454 | -5.294117 | 0.150032 | 4.145790 | -2.446929 | ... | 0.449788 | 0.160142 | -0.031747 | -0.358067 | -0.512304 | -0.431860 | 0.542046 | -0.429891 | 0.129443 | -0.421667 |
| 747 | 7.316390 | -7.549401 | -3.281177 | -5.320557 | 7.607325 | -0.349459 | -6.333130 | -2.863415 | -1.719797 | 2.862377 | ... | 0.039158 | 0.149544 | -0.735282 | 0.220999 | 0.444086 | -0.087249 | -0.062941 | 0.317693 | 0.220423 | 0.127270 |
| 748 | -4.471304 | -6.847694 | -9.406762 | -4.260997 | 2.071330 | 2.058631 | 2.733293 | -0.932200 | -0.403964 | 2.632530 | ... | -0.025071 | 0.134067 | 0.306671 | 0.381223 | 0.082204 | -0.573076 | -0.320897 | -0.533770 | 0.029683 | 0.137277 |
| 749 | -10.251017 | -3.028337 | 4.381681 | -1.659601 | 3.600789 | -2.654735 | -1.667317 | 2.144917 | -1.502747 | -0.583928 | ... | 0.258883 | 0.294049 | 0.140972 | -0.600434 | 0.387110 | -0.327360 | -0.257897 | -0.747806 | 0.086976 | 0.010088 |
750 rows × 609 columns
CV-SIFT - Clustering - Kmeans¶
Nous allons maintenant vérifier s'il serait faisable de labelliser automatiquement nos photos. Pour cela nous n'allons pas utiliser d'algorithme de classification supervisée (pour rappel nous avons fait le choix d'utiliser peu de données), mais un modèle de clustering. Cela nous permettra d' analyser, à ce stade préliminaire, si les features que nous venons de créer permettent de regrouper nos catégories dans des clusters.
Étapes de l'étude de faisabilité :
- algorithme de clustering avec, pour rappel, K = le nombre catégories de photo
- calcul de l'ARI entre les labels des clusters et les catégories
- projection de nos données en 2D grâce à un TSNE
- représentation des clusters et des vraies catégories sur cet espace
L'ARI pour cette méthode SIFT n'est pas très bon : < 0.15 ... Notre clustering n'est pas une réussite. On le remarque assez bien sur la projection T-SNE :
# use existing .joblib
load("mySaves/figures/figSIFTscatter.joblib")
La seule classe intéressante en termes de résultat est "menu". On peut vérifier également cela en remplaçant quelques points par leurs images respectives :
# use existing .joblib
load("mySaves/figures/figSIFTphotos.joblib")
Le SIFT ne nous permet pas vraiment de conclure positivement quant à l'opportunité de développer un modèle de classification supervisée... Essayons maintenant avec du Transfer Learning :
PARTIE II.B - ANALYSER LES PHOTOS POUR DÉTERMINER LES CATÉGORIES DES PHOTOS - TRANSFER LEARNING¶
CV-TL - Utiliser un VGG16 pré-entraîné comme un extracteur de features autonome¶
Ici les étapes de traitement diffèrent un peu :
- Charger nos photos et les préparer pour le
VGG16:- les charger à la taille 224 x 224
- les convertir en array
- les rassembler en un seul array
- les convertir en BGR
- centrer chaque canal de couleur
- Utiliser un
VGG16déjà entraîné comme un extracteur de features :- retirer la dernière couche fully-connected (celle contenant la fonction d'activation Softmax nous donnant normalement nos scores par classe)
- se servir de la sortie de la couche précédente comme features
- Appliquer également une réduction de dimensions par
PCA
# load existing .joblib
vgg16Features = load("mySaves/transferLearningFeatures/reducedVGG16featuresDf.joblib")
vgg16Features
| C0 | C1 | C2 | C3 | C4 | C5 | C6 | C7 | C8 | C9 | ... | C658 | C659 | C660 | C661 | C662 | C663 | C664 | C665 | C666 | C667 | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | -0.846850 | -11.891654 | -6.117408 | -0.321572 | -4.291137 | -10.926862 | 6.937528 | -4.013148 | 1.305520 | -6.764181 | ... | 0.275987 | 0.232464 | 0.458611 | 0.543960 | 0.558130 | 0.222813 | -0.290001 | -0.539138 | 0.441324 | -0.017959 |
| 1 | 5.037025 | -13.103478 | -21.579805 | -3.339313 | 17.819710 | -8.520625 | 8.204929 | -10.765649 | 9.014797 | 3.520934 | ... | 0.055822 | -0.086640 | 0.150894 | -0.264949 | -0.387565 | -0.202633 | 0.066698 | 0.056778 | 0.600761 | 0.214788 |
| 2 | 9.072474 | -11.708897 | -6.724650 | 3.355097 | -4.021892 | -4.834295 | 3.494538 | -10.439341 | 1.280492 | -3.690792 | ... | 0.361371 | 0.752302 | 1.039641 | -0.582730 | 1.270271 | -0.051765 | -0.014603 | -0.750147 | -0.315628 | -0.322555 |
| 3 | 7.830352 | -15.519650 | -2.015309 | -5.825803 | -3.770918 | -4.440335 | -0.899613 | 2.694433 | -7.181771 | 2.514869 | ... | -1.738357 | 0.746954 | -0.229128 | 0.235237 | 1.069740 | 1.198662 | -1.320371 | -0.212548 | 1.796935 | -0.858946 |
| 4 | 9.490551 | -11.651093 | -16.992430 | 10.883677 | 5.122124 | -1.120993 | -1.717786 | 4.987165 | -8.951221 | 3.050616 | ... | -0.412974 | 0.353927 | -0.452747 | 0.155640 | 0.352392 | -0.223588 | 0.204226 | -0.338285 | -0.314118 | 0.486172 |
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
| 745 | -16.692192 | -3.265703 | 8.305225 | -0.597627 | 2.250128 | -0.669426 | 2.502982 | 6.066919 | 13.828831 | -2.058198 | ... | 1.354470 | 0.674851 | -0.447909 | -0.068569 | 0.513947 | 1.289411 | 0.007111 | 0.686466 | 0.755736 | 0.022067 |
| 746 | -21.897047 | 3.863275 | 19.063433 | 17.120266 | 2.947626 | 0.455507 | -7.644225 | 8.272452 | -10.308472 | 6.402745 | ... | -1.173490 | 0.004134 | -0.166072 | 0.161261 | 0.581118 | -0.153856 | 0.496201 | -0.710096 | 0.551586 | 0.497768 |
| 747 | -22.948324 | -3.751245 | 14.161858 | 5.887580 | -6.787694 | -16.807522 | 16.557436 | 13.814086 | -10.116261 | 2.190607 | ... | -0.717053 | -0.321911 | 0.160335 | -0.334540 | -1.048403 | 0.455534 | 0.471592 | 0.863454 | -0.557402 | -0.150942 |
| 748 | -5.360372 | -6.883369 | 3.313624 | -0.890294 | -5.239982 | 4.500829 | 19.392597 | 4.678927 | -11.867502 | 25.445805 | ... | 0.773634 | -0.563206 | 0.248252 | 0.153675 | 0.176950 | 0.101078 | 0.064125 | -0.137391 | -0.173470 | 0.014518 |
| 749 | -20.396402 | -1.092745 | 12.486311 | 6.833673 | 4.355201 | -3.832258 | -0.639914 | 3.972427 | 4.758598 | 14.303320 | ... | 0.424066 | -0.625958 | 0.875880 | 0.002121 | 1.905194 | 0.037119 | 0.323795 | 0.278523 | 2.208139 | -0.139011 |
750 rows × 668 columns
CV-TL - Clustering - Kmeans¶
L'ARI pour cette méthode Transfer Learning est bien meilleur : > 0.70 ... On le remarque assez bien sur la projection T-SNE :
# use existing .joblib
load("mySaves/figures/figTLscatter.joblib")
La correspondance est bonne. On peut vérifier également cela en remplaçant quelques points par leurs images respectives :
# use existing .joblib
load("mySaves/figures/figTLphotos.joblib")
Le VGG16 en tant qu'extracteur de features donne de bien meilleurs résultats.
On peut conclure qu'il serait opportun de poursuivre le projet avec le développement d'un modèle de classification supervisée (avec bien plus de photos).